package com.blb.edusearchservice.dao;

import com.alibaba.fastjson.JSON;
import com.blb.common.entity.Course;
import com.blb.common.entity.PageEntity;
import com.blb.edusearchservice.entity.ElasticsearchEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.query.HighlightQueryBuilder;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * ES索引操作类
 */
@Slf4j
@Repository
public class ElasticsearchDAO {

    //ES的Rest客户端
    @Autowired
    private RestHighLevelClient client;

    /**
     * 判断索引是否存在
     */
    public boolean exists(String indexName) throws IOException {
        //创建查询请求
        GetIndexRequest request = new GetIndexRequest(indexName);
        //判断索引是否存在
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        return exists;
    }

    /**
     * 删除索引
     */
    public void delete(String indexName) throws IOException {
        //创建删除请求
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        //判断索引是否存在
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
        log.info("删除成功{}",indexName);
    }

    /**
     * 创建索引
     */
    public void create(String indexName) throws IOException {
        //创建请求
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        //判断索引是否存在
        AcknowledgedResponse create = client.indices().create(request, RequestOptions.DEFAULT);
        log.info("创建成功{}",indexName);
    }

    /**
     * 批量插入数据
     */
    public void insertBatch(String indexName, List<ElasticsearchEntity> entities) throws IOException {
        //创建批量任务
        BulkRequest request = new BulkRequest(indexName);
        //遍历集合，添加到批量任务中
        entities.forEach(elasticsearchEntity -> {
            //批量插入数据 id指定文档id，source指定文档内容
            request.add(new IndexRequest()
                    .id(elasticsearchEntity.getId())
                    .source(JSON.toJSONString(elasticsearchEntity.getData()), XContentType.JSON));
        });
        //执行批量插入请求
        client.bulk(request,RequestOptions.DEFAULT);
        log.info("批量插入成功{}",indexName);
    }

    /**
     * 多条件搜索方法
     */
    public PageEntity search(String indexName,
                             Map<String,String> filters,
                             Map<String,String> sort,
                             int from,int size,Class clazz,
                             String... highlightFields) throws Exception {
        //创建搜索请求
        SearchRequest searchRequest = new SearchRequest(indexName);
        //搜索资源创建器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //创建布尔搜索
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //遍历搜索条件
        if(filters != null && !filters.isEmpty()){
            for(String key : filters.keySet()){
                //添加搜索条件，短语搜索
                String value = filters.get(key);
                if(StringUtils.isNotEmpty(value)) {
                    boolQuery.filter(QueryBuilders.matchPhraseQuery(key, value));
                }
            }
        }
        //判断是否设置了过滤条件，有就设置为布尔搜索
        if(boolQuery.filter().size() > 0){
            searchSourceBuilder.query(boolQuery);
        }
        //设置排序方式
        if(sort != null && !sort.isEmpty()){
            String field = sort.get("field");
            String type = sort.get("type");
            searchSourceBuilder.sort(new FieldSortBuilder(field).order("asc".equals(type) ? SortOrder.ASC : SortOrder.DESC));
        }
        //设置分页
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);
        //创建高亮显示生成器
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //设置高亮字段
        for(String highlight : highlightFields){
            highlightBuilder.field(highlight);
        }
        //设置高亮html标签
        highlightBuilder.preTags("<span style='color:red'>").postTags("</span>");
        //设置高亮生成器
        searchSourceBuilder.highlighter(highlightBuilder);
        //设置搜索资源
        searchRequest.source(searchSourceBuilder);
        //发送搜索请求，获得响应结果
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //获得数据，遍历数据
        SearchHit[] hits = response.getHits().getHits();
        List data = new ArrayList();
        for (SearchHit hit : hits){
            //把json字符串转为java对象
            Object obj = JSON.parseObject(hit.getSourceAsString(), clazz);
            data.add(obj);
            //获得高亮字段
            Map<String, HighlightField> fields = hit.getHighlightFields();
            for(String field : fields.keySet()){
                //用反射读取当前对象的属性
                Field declaredField = clazz.getDeclaredField(field);
                //将字段的值替换为高亮文字
                declaredField.setAccessible(true);
                Text[] fragments = fields.get(field).fragments();
                if(declaredField != null && fragments != null && fragments.length > 0){
                    declaredField.set(obj,fragments[0].string());
                }
            }
        }
        //获得分页数据
        PageEntity pageEntity = new PageEntity();
        pageEntity.setCurrent(from);
        pageEntity.setTotal((int) response.getHits().getTotalHits().value);
        pageEntity.setSize(size);
        pageEntity.setData(data);
        return pageEntity;
    }

    /**
     * 添加或更新信息
     * @param index
     * @param entity
     */
    public void saveOrUpdate(String index,ElasticsearchEntity entity) throws IOException {
        IndexRequest request = new IndexRequest(index);
        request.id(entity.getId()).source(JSON.toJSONString(entity.getData()),XContentType.JSON);
        client.index(request,RequestOptions.DEFAULT);
        log.info("保存数据完成：{}",entity);
    }

    /**
     * 删除信息
     */
    public void removeById(String index,String id) throws IOException {
        //按id进行条件查询并删除文档
        DeleteByQueryRequest request = new DeleteByQueryRequest(index);
        request.setQuery(QueryBuilders.termQuery("id",id));
        client.deleteByQuery(request,RequestOptions.DEFAULT);
        log.info("删除数据完成：{}",id);
    }
}
